package com.liusu.crypto.transmit;

import cn.hutool.core.text.AntPathMatcher;
import com.alibaba.fastjson2.JSONObject;
import com.liusu.common.utils.servlet.RequestWrapper;
import com.liusu.common.utils.servlet.ResponseWrapper;
import com.liusu.common.utils.servlet.ServletUtils;
import com.liusu.common.utils.sm.Sm2Utils;
import com.liusu.common.utils.sm.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Order(-97)
@WebFilter(urlPatterns = {"/*"})
//TODO 不启用加解密
//@Component
public class AutoCryptoRequestFilter extends OncePerRequestFilter {

    /**
     * 白名单路径
     */
    private static final List<String> whitelistPaths = Arrays.asList("/sys/randomImage/**", "/**/exportXls/**", "/**/importExcel/**");

    /**
     * 路径匹配
     */
    private static final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
        RequestWrapper mReq = new RequestWrapper(request);
        ResponseWrapper mRep = new ResponseWrapper(response); // 包装响应对象 resp 并缓存响应数据


        //前端标识请求是加密的并且body中有值才进行解密
        if ("1".equals(request.getHeader("isCrypto")) && StringUtils.isNotEmpty(mReq.getBody())) {
            String sm4KeyCrypto = mReq.getHeader("SM4key");
            if (StringUtils.isNotEmpty(sm4KeyCrypto)) {
                String sm4Key = Sm2Utils.decrypt(sm4KeyCrypto);
                sm4Key = sm4Key.replace("\"", "");
                String decrypt = Sm4Utils.decrypt(mReq.getBody(), sm4Key);
                mReq.setBody(decrypt);
            }
        }


        filterChain.doFilter(mReq, mRep);

        boolean isWhitelisted = isWhitelisted(request);
        //文件格式不进行处理
        if ("application/vnd.ms-excel;charset=UTF-8".equals(response.getContentType())
                || isWhitelisted) {
            response.setHeader("isCrypto", "0");
            filterChain.doFilter(request, response);
        } else {
            // 获取缓存的响应数据
            byte[] bytes = mRep.getBytes();
            String repStr = new String(bytes, StandardCharsets.UTF_8);
            JSONObject repJson = JSONObject.parseObject(repStr);
            boolean success = repJson.get("success") == null || (boolean) repJson.get("success");
            int code = repJson.get("code") == null ? 0 : (int) repJson.get("code");

            //如果不在白名单，且返回结果是成功的进行加密
            if (success || code == 0) {
                String sm4Key = Sm4Utils.getSm4Key();
                String sm4KeyCrypto = Sm2Utils.encrypt(sm4Key);
                response.setHeader("SM4key", sm4KeyCrypto);
                response.setHeader("isCrypto", "1");
                repStr = Sm4Utils.encrypt(repStr, sm4Key);
                ServletUtils.writeJSON(response, repStr);
            } else {
                ServletUtils.writeJSON(response, repJson);
            }

        }
    }

    private boolean isWhitelisted(HttpServletRequest request) {
        //获取请求路径
        String requestUri = request.getRequestURI();
        //获取上下文路径
        String contextPath = request.getContextPath();
        //计算真实请求地址（忽略上下文路径）
        String path = requestUri.substring(contextPath.length());
        // 处理根路径
        if (path.isEmpty()) {
            path = "/";
        }
        //进行对比，如果当前路径在白名单中，则结束处理，不进行返回结果加密
        String finalPath = path;
        return whitelistPaths.stream()
                .anyMatch(pattern -> pathMatcher.match(pattern, finalPath));
    }
}
